package com.hotpot.ioc;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.lang3.StringUtils;

import com.hotpot.ioc.core.Ioc;
import com.hotpot.ioc.kit.ClassSearcher;
import com.hotpot.ioc.kit.PathKit;
import com.jfinal.aop.Enhancer;
import com.jfinal.log.Log;
import com.jfinal.plugin.IPlugin;

/**
 * 描述:仿spring ioc容器，存放所有注解@ioc的类的实例，并为实例中注解@Resource的属性，注入容器中已有的实例
 * @author myx
 * @date 2017年6月23日 下午4:20:42
 */

/*
//使用说明
public void configPlugin(Plugins plugins) {
    // ......
    //ioc插件       
     plugins.add(IocPlugin.me());
}
需要容器管理的类，添加@Ioc注解，需要被注入的属性，添加@Resource注解
@Ioc
public class Test{
	@Resource  //如果user对象被容器管理，会自动注入
	private User user;
}
*/

public class IocPlugin implements IPlugin {

	protected final Log log = Log.getLog(getClass());
    
    private List<String> includeJars = new ArrayList<String>();
    private boolean includeAllJarsInLib = false;
    private List<String> scanPackages = new ArrayList<String>();
    private String classpath = PathKit.getRootClassPath();
    private String libDir = PathKit.getWebRootLibPath();    
    
    private static final String multSign = "mult@";
    
    private static final String aopSupportSign = "$$aop";
    
    private static final IocPlugin iocContext = new IocPlugin();
    
    private IocPlugin(){}
    
    public static IocPlugin me(){
    	return iocContext;
    }
    /**
     * 存放实例
     */
	private static volatile Map<String, Object> instances = new HashMap<String, Object>();	

	/**
	 * @Title: addScanPackages
	 * @Description: 添加需要扫描的包，默认为扫描所有包
	 * @param packages
	 * @return
	 * @return: IocPlugin
	 */
    public IocPlugin addScanPackages(String... packages) {
        for (String pkg : packages) {
            scanPackages.add(pkg);
        }
        return this;
    }

    public IocPlugin addJars(List<String> jars) {
        if (jars != null) {
            includeJars.addAll(jars);
        }
        return this;
    }

    public IocPlugin addJars(String... jars) {
        if (jars != null) {
            for (String jar : jars) {
                includeJars.add(jar);
            }
        }
        return this;
    }
    
    public IocPlugin libDir(String libDir) {
        this.libDir = libDir;
        return this;
    }
    public IocPlugin includeAllJarsInLib(boolean includeAllJarsInLib) {
        this.includeAllJarsInLib = includeAllJarsInLib;
        return this;
    }    
	
    /**
     * 方法描述：获取实例
     * @param name
     * @return
     * @return: T
     */
	@SuppressWarnings("unchecked")
	public <T> T getInstance(String name){
		Object instance =  instances.get(name);
		if(instance==null)return null;
		if(instance instanceof String){
			return (T)lazyInstance((String)instance);
		}
		return (T)instance;
	}
	
	/**
	 * 方法描述：获取实例
	 * @param classzz
	 * @return
	 * @return: T
	 */
	@SuppressWarnings("unchecked")
	<T> T getInstance(Class<?> classzz){
		return (T)getInstance(classzz.getName());
	}

	/**
	 * 方法描述：单个注册
	 * @param instance
	 * @return: void
	 */
    public void registerClass(Class<?> instance){
    	registerClass(instance, false);
    }
    
    /**
     * 批量注册
     * @param instanceClasses
     * @return: void
     */
    public void registerClass(List<Class<?>> instanceClasses){
		for (Class<?> instance : instanceClasses) {
			registerClass((Class<?>) instance,true);
		}
		
		//为容器中的实例的属性注入值
		for (Map.Entry<String, Object> entry : instances.entrySet()) {
			Object iocInstance = entry.getValue();
			injectField(iocInstance);
		}     	
    }
    
    /**
     * 注册需要ioc管理的类
     * @param instance
     * @return: void
     */
    private void registerClass(Class<?> instance,boolean mult){
		if(Modifier.isAbstract(instance.getModifiers())){
			return;  //是抽象类，跳过处理
		}
		
		String name = null;
		
		// 获取注解对象
		Ioc serviceBind = (Ioc) instance.getAnnotation(Ioc.class);

		name = serviceBind.value().trim();
	
		if(name.equals(""))name = instance.getName();
	
		if (instances.get(name) == null) {
			if(serviceBind.mult()){
				instances.put(name, multSign+instance.getName()+(serviceBind.aop()?aopSupportSign:""));
				log.debug("IocPlugin注册： name = " + name + ", class = " + instance.getName()+",多例模式");
			}else{
				instances.put(name, instance.getName()+(serviceBind.aop()?aopSupportSign:""));
								
				if(!serviceBind.lazy()){
					lazyInstance(name);
				}else{
					log.debug(name+":延迟加载！");
				}
			}
		}else{
			log.error(instance.getName() + "注解错误，"+instances.get(name).getClass().getName()+" 已注册了标识["+name+"]!");
		}   
		

    }    

	/**
	 * 实例属性注入
	 * @param iocInstance
	 * @return: void
	 */
	private void injectField(Object iocInstance) {
		if(!(iocInstance instanceof String)){
			Field[] fields = null;
			Class<?> clazz = iocInstance.getClass();
	        while (clazz != Object.class) {
	            try {
	            	fields = clazz.getDeclaredFields();
	    			for(Field field:fields){
	    				Resource annotation = field.getAnnotation(Resource.class);
	    				if(annotation==null)continue;
	    				String serviceName = null;
	    				if(annotation.name().equals("")){
	    					serviceName = field.getType().getName();
	    				}else{
	    					serviceName = annotation.name().trim();
	    				}
	    				try {
	    					field.setAccessible(true);
	    					if(field.get(iocInstance)!=null)continue;//如果有值了，就不处理了
	    					Object fieldValue = getInstance(serviceName);
	    					if(fieldValue!=null){
	    					    field.set(iocInstance,fieldValue);
	    					    log.debug("为实例"+iocInstance.getClass().getName()+"注入属性["+serviceName+"]，属性值："+fieldValue.toString());
	    					}else{
	    						log.error("为实例"+iocInstance.getClass().getName()+"注入属性["+serviceName+"]出错，请确认配置是否正确！");
	    					}
	    					
	    				} catch (Exception e) {
	    					e.printStackTrace();
	    				} finally{
	    					field.setAccessible(false);
	    				}
	    			}	                
	            } catch (Exception e) {
	                log.error("为实例"+iocInstance.getClass().getName()+"注入属性出错！",e);
	            }finally{
	            	clazz = clazz.getSuperclass();	            	
	            }
	        }			
		}
	}
    
    /**
     * 懒加载
     * @param name
     * @return
     */
    private Object lazyInstance(String name){
    	Object instanceName = instances.get(name);
		if (instanceName != null&&instanceName instanceof String) {
			try {
				String instanceClass = (String)instanceName;
				Object iocInstance = null;
				if(instanceClass.startsWith(multSign)){
					if(instanceClass.endsWith(aopSupportSign)){
						iocInstance =(Object) Enhancer.enhance(Class.forName(StringUtils.substringBetween(instanceClass,multSign, aopSupportSign)));
					}else{					
				        iocInstance = Class.forName(StringUtils.substringAfter(instanceClass, multSign)).newInstance();
					}
				}else{
					synchronized (this) {
						instanceName = instances.get(name);
						if(instanceName instanceof String){
							if(instanceClass.endsWith(aopSupportSign)){
								iocInstance = (Object) Enhancer.enhance(Class.forName(StringUtils.substringBefore(instanceClass, aopSupportSign)));
							}else{					
						        iocInstance = Class.forName(StringUtils.substringBefore(instanceClass, aopSupportSign)).newInstance();
							}							
							instances.put(name, iocInstance);
						}
					}
				}
				log.debug("IocPlugin注册： name = " + name + ", class = " + instanceName);
				injectField(iocInstance);				
				return iocInstance;
			} catch (Exception e) {
				throw new RuntimeException(e);
			}
		}else{
			throw new RuntimeException(name + " not found in IocPlugin!");
		}
    }

	@Override
	public boolean start() {
		List<Class<?>> instanceClasses = ClassSearcher.of(Ioc.class).libDir(libDir).classpath(classpath)
                .scanPackages(scanPackages).injars(includeJars).includeAllJarsInLib(includeAllJarsInLib).search();
		registerClass(instanceClasses);
		
		log.debug(instances.toString());		
		return true;
	}

	@Override
	public boolean stop() {
		return true;
	}
    
    
    
}
